其他
Cglib动态代理探索(ASM,Spring)
本期来和小伙伴们分享这个 Cglib
动态代理啦~ (~ ̄(OO) ̄)ブ
文章概览
基本介绍
CGLIB(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.7</version>
</dependency>
MethodInterceptor
接口,并重写 intercept
方法,感觉这一步和 JDK动态代理 差不多 😄// 代理类class文件存入本地磁盘方便我们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./code");
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(UserService.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MyInterceptor());
// 创建代理对象
UserService userService = (UserService)enhancer.create();
// 通过代理对象调用目标方法
userService.say();
userService.finalMethod();
UserService.staicMethod();
say
方法被增强了,被 final
或者 static
修饰的无法被代理
源码探索
code
目录,而且居然生成了三份字节码文件UserService
,其他两个文件则 继承 了 FastClass
类debug
一下就可以猜出来啦~say
方法处打个断点🐷,然后可以看到这里在调用 invoke
方法时出现了这个 14 var10000
是表示第二个字节码文件CGLIB$say$0
方法如下图红框中所示~ 。UserService
类的 say
方法~
FastClass
FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法
MethodInterceptor
的 intercept
方法中,我们除了使用方法代理类 MethodProxy
的 invokeSuper
外,它还有一个 invoke
方法可以给我们使用。invoke
方法时,却出现了 栈溢出 的情况!如图🐖ASM
,这是一个小而快的字节码处理框架,用来转换字节码并生成新的类,是一个非常有意思的技术!(不过这我真不会了…… 哈哈哈 为啥总有种欠打的感觉~ 可能蕉太狼的表情包有毒 哈哈)MethodProxy
是 Cglib
中非常重要的一环!MethodProxy
中通过FastClassInfo
保存FastClass
和调用方法的index
下标只代理 非final 或者 非static 方法 如果需要对所有的方法进行增强调用,可以使用 invokeSuper
(第一个参数为增强的对象)。如果只想对调用的方法进行增强,不增强该方法内部使用到的同类中的其他方法,可以使用 invoke
方法 (第一个参数为原始对象)
小图小结
FassClass
文件(f2名字最长~)如果传如的是代理对象,则会选择 f2 , 如果是原始对象,则会选择 f1 , 接着根据方法的下标 index
去文件中查找对应的方法,并进行调用~
CGlib比JDK快?
使用 CGLiB
实现动态代理,CGLib
底层采用ASM
字节码生成框架,使用字节码技术生成代理类, 在jdk6
之前比使用Java
反射效率要高。唯一需要注意的是,CGLib
不能对声明为final
的方法进行代理, 因为CGLib
原理是动态生成被代理类的子类。在 jdk6
、jdk7
、jdk8
逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率 高于 CGLIB代理效率。只有当进行大量调用的时候,jdk6
和jdk7
比CGLIB
代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次 jdk版本升级,JDK代理效率 都得到提升,而 CGLIB代理效率 确有点跟不上步伐。—— https://blog.csdn.net/m0_57711043/article/details/117020684
CGLIB和Jdk动态代理的区别
Jdk动态代理 只能对实现了接口的类进行代理 ,而 CGLIB 只能代理非final 类,因为它要去生成代理类的子类(没有 Jdk动态代理 的局限性) Jdk动态代理 生成的字节码文件中,代理类都继承了 Proxy
类,并实现了接口,而且生成字节码的效率比CGLIB 高(cglib 一式三份 ,jdk只有一份)Jdk动态代理 中有个显眼的变量 h
,debug
时多留意下就可以了~CGLIB 底层使用 ASM
框架来操作字节码文件,同时利用FassClass
机制,实现对方法的快速索引并直接调用,不用经过反射,而 Jdk动态代理 则采用反射API进行操作
ASM
介绍
ASM
是一个通用的Java
字节码操作和分析框架。
cglib
有这么一张图(来自网络)ASM
的用途:
AOP 结合 javaagent
可以实现 热部署,以及日志追踪(类似SkyWalking
)arthas
的运行也是基于这个javaagent
和ASM
的,同时还使用到JVMTI(JVM Tool Interface)
Spring AOP
Spring
中的使用Spring
中,有下面这么两个代理类:JdkDynamicAopProxy
和 CglibAopProxy
JdkDynamicAopProxy
CglibAopProxy
enhancer
SpringAOP 小结
在 Spring
中:
对象实现接口,使用 JDK动态代理
对象没有实现接口,使用 Cglib动态代理
可以强制使用CGlib ,配置
@EnableAspectJAutoProxy(proxyTargetClass = true)
或者 spring.aop.proxy-target-class=true
总结
这次的内容比较多,就弄成了个思维导图啦,最主要的是 cglib
特点, FassClass
机制 以及 Cglib和JDK动态代理的对比 ,还有 SpringAOP
这四个,扩展的有这个 ASM
😄
小伙伴们如果需要这个思维导图可以直接在公众号后台回复 "代理模式2" 获取😝
Springboot
中 AOP
失效的原因 😄最后
如果你觉得本篇文章还不错的话,那拜托再点点赞支持一下呀😝 让我们开始这一场意外的相遇吧!~ 欢迎留言!谢谢支持!ヾ(≧▽≦*)o 冲冲冲!! 我是4ye 咱们下期应该……很快再见!! 😆